#include <getopt.h>
#include "DefaultLogger.h"
#include "TextUI.h"
#include "InvokeMode.h"
#include "CLInstSrc.h"
#include "TextInstSrc.h"
#include "UIInstructionImp.h"
#include "InvokeInstruction.h"
#include "InvokeInstructionImp.h"
#include "UIInitialization.h"

void EnterTuiBatchMode(const std::string &file);

class EnterTuiCommand : public Command
{
    virtual void Do(std::vector<std::string> &args);
};

void EnterTuiCommand::Do(std::vector<std::string> &args)
{
    EnterTuiMode();
}

class EnterTuiBatchCommand : public Command
{
    virtual void Do(std::vector<std::string> &args);
};

void EnterTuiBatchCommand::Do(std::vector<std::string> &args)
{
    if (args.empty()) {
        printf("file not found\n");
        return;
    }
    EnterTuiBatchMode(args[0]);
}

void InitializeInstructions(std::shared_ptr<MyLogger> &logger, std::shared_ptr<PersistStorage> config)
{
    InitInstructionMap();
    std::shared_ptr<Command> tuiCmd(new EnterTuiCommand);
    std::shared_ptr<Command> tuiBatchCmd(new EnterTuiBatchCommand);
    InitInvokeInstructionMap(tuiCmd, tuiBatchCmd);

    Instruction::SetLogger(logger);
    InvokeMode::SetLogger(logger);
    UserInterface::SetLogger(logger);

    // TODO config init
    Instruction::SetConfig(config);
    UserInterface::SetConfig(config);
}

void EnterTuiModeCommon(std::unique_ptr<InstructionSrc> &instSrc)
{
    UserInterface::SetInstSrc(instSrc);
    TextUI tui("Welcome to Demo System");
    tui.MainLoop();
}

void EnterTuiMode(void)
{
    std::unique_ptr<InstructionSrc> instSrc(new CLInstSrc);
    EnterTuiModeCommon(instSrc);
}

void EnterTuiBatchMode(const std::string &file)
{
    std::unique_ptr<InstructionSrc> instSrc(new TextInstSrc(file));
    EnterTuiModeCommon(instSrc);
}

void EnterInvokeMode(int argc, char *const argv[])
{
    std::vector<std::string> args;
    int optNum = getopt(argc, argv, InvokeInstruction::GetInvokeOptStr().c_str());
    char optChar = static_cast<char>(optNum);
    if (optChar == '?') {
        printf("option not found\n");
        return;
    }
    InvokeMode invokeMode;
    std::string opt(" ");
    opt[0] = optChar;
    args.push_back(opt);
    for (int i = 2; i < argc; i++) {
        args.push_back(argv[i]);
    }
    invokeMode.Execute(args);
}